/*
 Copyright (C) THL A29 Limited, a Tencent company. All rights reserved.

 SPDX-License-Identifier: Apache-2.0
*/

package safemath

import (
	"math/big"
)

type SafeUint256 big.Int

var maxSafeUint256 *SafeUint256
var minSafeUint256 *SafeUint256

func init() {
	x := big.NewInt(1)
	x = x.Lsh(x, 256).Sub(x, big.NewInt(1))
	maxSafeUint256 = (*SafeUint256)(x)
	minSafeUint256 = (*SafeUint256)(big.NewInt(0))
}

func ParseSafeUint256(x string) (*SafeUint256, bool) {
	z := big.NewInt(0)
	if x == "" {
		return (*SafeUint256)(z), true
	}
	z, ok := z.SetString(x, 10)
	if !ok || z.Cmp((*big.Int)(maxSafeUint256)) > 0 || z.Cmp((*big.Int)(minSafeUint256)) < 0 {
		return nil, false
	}
	return (*SafeUint256)(z), true
}

func SafeAdd(x, y *SafeUint256) (*SafeUint256, bool) {
	z := big.NewInt(0)
	z = z.Add((*big.Int)(x), (*big.Int)(y))
	if z.Cmp((*big.Int)(maxSafeUint256)) > 0 {
		return nil, false
	}
	return (*SafeUint256)(z), true
}

func SafeSub(x, y *SafeUint256) (*SafeUint256, bool) {
	if (*big.Int)(x).Cmp((*big.Int)(y)) < 0 {
		return nil, false
	}
	return (*SafeUint256)((*big.Int)(x).Sub((*big.Int)(x), (*big.Int)(y))), true
}

func SafeMul(x, y *SafeUint256) (*SafeUint256, bool) {
	z := (*big.Int)(x).Mul((*big.Int)(x), (*big.Int)(y))
	if z.Cmp((*big.Int)(maxSafeUint256)) > 0 || z.Cmp((*big.Int)(minSafeUint256)) < 0 {
		return nil, false
	}
	return (*SafeUint256)(z), true
}

func SafeDiv(x, y *SafeUint256) *SafeUint256 {
	return (*SafeUint256)((*big.Int)(x).Div((*big.Int)(x), (*big.Int)(y)))
}

func (x *SafeUint256) Cmp(y *SafeUint256) int {
	return (*big.Int)(x).Cmp((*big.Int)(y))
}

func (x *SafeUint256) ToString() string {
	return (*big.Int)(x).String()
}

func (x *SafeUint256) IsMaxSafeUint256() bool {
	if x.Cmp(maxSafeUint256) == 0 {
		return true
	}

	return false
}
